/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.erickpardal.game.fpsdemo;

import com.erickpardal.game.fpsdemo.scene.SkyDome;
import com.erickpardal.game.fpsdemo.util.VBOUtil;
import com.jme.bounding.BoundingBox;
import com.jme.image.Image.Format;
import com.jme.image.Texture;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.NodeHandler;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.CameraNode;
import com.jme.scene.Node;
import com.jme.scene.Skybox;
import com.jme.scene.state.CullState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jme.util.export.binary.BinaryImporter;
import com.jme.util.resource.ResourceLocatorTool;
import com.jme.util.resource.SimpleResourceLocator;
import com.jmex.effects.LensFlare;
import com.jmex.effects.LensFlareFactory;
import com.jmex.game.state.CameraGameState;
import com.jmex.model.collada.ColladaImporter;
import com.jmex.model.converters.MaxToJme;
import com.jmex.model.converters.Md2ToJme;
import com.jmex.model.converters.ObjToJme;
import com.jmex.model.converters.MilkToJme;
import com.jmex.model.converters.X3dToJme;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.AbstractHeightMap;
import com.jmex.terrain.util.FaultFractalHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Random;
import javax.swing.ImageIcon;

/**
 *
 * @author Erick Pardal
 */
public class IngameState extends CameraGameState {

    private TerrainPage terrainPage;
    private Skybox skyBox;
    //private SkyDome dome;
    private InputHandler input;
    private Player player;
    private CameraNode camNode;

    public IngameState() {
        super("InGame");

        initSkyBox();
        //initSkyDome();//Erro
        initTerrain();
        initLight();
        //initModels();
        initPlayer();

        KeyBindingManager.getKeyBindingManager().set(
                "exit", KeyInput.KEY_ESCAPE);

        CullState cs = DisplaySystem.getDisplaySystem().getRenderer().createCullState();
        cs.setCullFace(CullState.Face.Back);
        cs.setNeedsRefresh(true);
        cs.setEnabled(true);
        rootNode.setRenderState(cs);

        ZBufferState zbs = DisplaySystem.getDisplaySystem().getRenderer().createZBufferState();
        zbs.setEnabled(true);
        rootNode.setRenderState(zbs);

        cam.update();
        rootNode.updateGeometricState(0.0f, true);
        rootNode.updateRenderState();
        rootNode.updateWorldBound();
    }

    private void initPlayer() {
        try {
            ResourceLocatorTool.addResourceLocator(
                    ResourceLocatorTool.TYPE_TEXTURE,
                    new SimpleResourceLocator(getClass().getClassLoader().getResource(
                    "com/erickpardal/game/fpsdemo/data/models/weapon/ak47/")));
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        player = new Player();

        camNode = new CameraNode("Camera", cam);
        input = new NodeHandler(camNode, 50, 1f);
        camNode.getCamera().setLocation(new Vector3f(0, 0, 0));
        camNode.setLocalTranslation(new Vector3f(0, 0, 0));
        player.setCamera(camNode);
        cam.update();

        rootNode.attachChild(camNode);

        Node weapon = loadMilkModel(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/models/weapon/ak47/HUD.ms3d"));
        weapon.setLocalTranslation(new Vector3f(0, -10, 0));
        Quaternion rot = new Quaternion();
        rot.fromAngleAxis(FastMath.DEG_TO_RAD * 90 * 2, new Vector3f(0, 1, 0));
        weapon.setLocalRotation(rot);
        weapon.setLocalScale(0.047f);

        TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        ts.setEnabled(true);
        ts.setTexture(
                TextureManager.loadTexture(
                getClass().getClassLoader().getResource(
                "com/erickpardal/game/fpsdemo/data/models/weapon/ak47/ak_tex.jpg"),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear));
        ts.setTexture(
                TextureManager.loadTexture(
                getClass().getClassLoader().getResource(
                "com/erickpardal/game/fpsdemo/data/models/weapon/ak47/hand_tex.jpg"),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear));
        weapon.setRenderState(ts);

        player.setHUDWeapon(weapon);

        player.init(rootNode);
    }

    /*private void initModels() {

        //load metal barrel
        try {
            ResourceLocatorTool.addResourceLocator(
                    ResourceLocatorTool.TYPE_TEXTURE,
                    new SimpleResourceLocator(getClass().getClassLoader().getResource(
                    "com/erickpardal/game/fpsdemo/data/textures/scene/metal_barrel/")));
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        //load3DSModel(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/models/scene/metal_barrel/barrel.3DS"), new Vector3f(200, terrainPage.getHeight(200, 0)+50, 0));

        loadObjModel(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/models/scene/metal_barrel/barrel.obj"), new Vector3f(200, terrainPage.getHeight(200, 0), 0), new Vector3f(0.4f, 0.4f, 0.4f));

        //milk
        try {
            ResourceLocatorTool.addResourceLocator(
                    ResourceLocatorTool.TYPE_TEXTURE,
                    new SimpleResourceLocator(getClass().getClassLoader().getResource(
                    "jmetest/data/model/msascii/")));
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        loadMilkModel(getClass().getClassLoader().getResource("jmetest/data/model/msascii/run.ms3d"), new Vector3f(0, terrainPage.getHeight(0, 200) + 50, 200), new Vector3f(0.3f, 0.3f, 0.3f));

        //swat officer
        try {
            ResourceLocatorTool.addResourceLocator(
                    ResourceLocatorTool.TYPE_TEXTURE,
                    new SimpleResourceLocator(getClass().getClassLoader().getResource(
                    "com/erickpardal/game/fpsdemo/data/textures/human/swat/")));
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        loadColladaModel(getClass().getClassLoader().getResourceAsStream(
                "com/erickpardal/game/fpsdemo/data/models/human/swat/swat.dae"),
                getClass().getClassLoader().getResourceAsStream(
                "com/erickpardal/game/fpsdemo/data/models/human/swat/swat.dae"),
                new Vector3f(100, terrainPage.getHeight(100, 100) + 50, 100), new Vector3f(1f, 1f, 1f));
        //colt
        try {
            ResourceLocatorTool.addResourceLocator(
                    ResourceLocatorTool.TYPE_TEXTURE,
                    new SimpleResourceLocator(getClass().getClassLoader().getResource(
                    "com/erickpardal/game/fpsdemo/data/textures/weapon/colt/")));
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        load3DSModel(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/models/weapon/colt/colt-xm177.3DS"), new Vector3f(0, terrainPage.getHeight(0, 0) + 40, 0), new Vector3f(.04f, .04f, .04f));
        //loadObjModel(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/models/weapon/colt/colt-xm177.obj"), new Vector3f(50, terrainPage.getHeight(50, 50)+40, 50), new Vector3f(0.4f,0.4f,0.4f));

        //AR 10B
        try {
            ResourceLocatorTool.addResourceLocator(
                    ResourceLocatorTool.TYPE_TEXTURE,
                    new SimpleResourceLocator(getClass().getClassLoader().getResource(
                    "com/erickpardal/game/fpsdemo/data/textures/weapon/ar_10b/")));
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        load3DSModel(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/models/weapon/ar_10b/AR10B.3DS"), new Vector3f(50, terrainPage.getHeight(50, 0) + 40, 0), new Vector3f(.04f, .04f, .02f));
    //loadObjModel(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/models/weapon/ar_10b/AR10B.obj"), new Vector3f(100, terrainPage.getHeight(100, 50)+40, 50), new Vector3f(0.3f,0.4f,0.4f));
    }

    private Node loadMd2Model(URL modelToLoad) {
        try {
            Md2ToJme converter = new Md2ToJme();
            ByteArrayOutputStream BO = new ByteArrayOutputStream();
            converter.convert(modelToLoad.openStream(), BO);

            Node r = (Node) BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            return r;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private Node loadX3DModel(URL modelToLoad) {
        try {
            X3dToJme converter = new X3dToJme();
            ByteArrayOutputStream BO = new ByteArrayOutputStream();
            converter.convert(modelToLoad.openStream(), BO);

            Node r = (Node) BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            return r;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private void loadColladaModel(InputStream model, InputStream animation, Vector3f pos, Vector3f scale) {
        try {
            ColladaImporter.load(model, "model");
            SkinNode sn = ColladaImporter.getSkinNode(ColladaImporter.getSkinNodeNames().get(0));
            Bone skel = ColladaImporter.getSkeleton(ColladaImporter.getSkeletonNames().get(0));
            ColladaImporter.cleanUp();

            ColladaImporter.load(animation, "anim");
            ArrayList<String> animations = ColladaImporter.getControllerNames();
            if (animations != null) {
            System.out.println("Number of animations: " + animations.size());
            for (int i = 0; i < animations.size(); i++) {
            System.out.println(animations.get(i));
            }
            //Obtain the animation from the file by name
            BoneAnimation anim1 = ColladaImporter.getAnimationController(animations.get(0));

            //set up a new animation controller with our BoneAnimation
            AnimationController ac = new AnimationController();
            ac.addAnimation(anim1);
            ac.setRepeatType(Controller.RT_WRAP);
            ac.setActive(true);
            ac.setActiveAnimation(anim1);

            //assign the animation controller to our skeleton
            skel.addController(ac);
            }
            Node n = ColladaImporter.getModel();

            ColladaImporter.cleanUp();

            //Node n = new Node();
            //n.attachChild(sn);
            //n.attachChild(skel);
            n.setLocalScale(scale);
            n.setLocalTranslation(pos);

            rootNode.attachChild(n);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }*/

    private Node loadMilkModel(URL modelToLoad) {
        try {
            MilkToJme converter = new MilkToJme();
            ByteArrayOutputStream BO = new ByteArrayOutputStream();
            converter.convert(modelToLoad.openStream(), BO);
            Node model = (Node) BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            model.setModelBound(new BoundingBox());
            model.updateModelBound();
            return model;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /*private void loadMilkModel(URL modelToLoad, Vector3f pos, Vector3f scale) {
        try {
            MilkToJme converter = new MilkToJme();
            ByteArrayOutputStream BO = new ByteArrayOutputStream();
            converter.convert(modelToLoad.openStream(), BO);
            Node model = (Node) BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            model.setLocalScale(scale);
            model.setLocalTranslation(pos);
            rootNode.attachChild(model);
            rootNode.updateGeometricState(0.0f, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void loadObjModel(URL modelToLoad, Vector3f pos, Vector3f scale) {
        try {
            ObjToJme converter = new ObjToJme();
            converter.setProperty("mtllib", modelToLoad);
            ByteArrayOutputStream BO = new ByteArrayOutputStream();
            converter.convert(modelToLoad.openStream(), BO);
            Node model = (Node) BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            model.setModelBound(new BoundingBox());
            model.updateModelBound();
            model.setLocalScale(scale);
            model.setLocalTranslation(pos);
            rootNode.attachChild(model);
            rootNode.updateGeometricState(0.0f, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void load3DSModel(URL modelToLoad, Vector3f pos, Vector3f scale) {
        try {
            MaxToJme C1 = new MaxToJme();
            ByteArrayOutputStream BO = new ByteArrayOutputStream();
            C1.convert(new BufferedInputStream(modelToLoad.openStream()), BO);
            Node model = (Node) BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            model.setLocalTranslation(pos);
            model.setLocalScale(scale);
            //basic animation
            if (model.getChild(0).getControllers().size() != 0) {
                model.getChild(0).getController(0).setSpeed(20);
            }
            rootNode.attachChild(model);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }*/
    
    /*private void initSkyDome() {
        dome = new SkyDome();
    }*/

    private void initLight() {
        LightState lightState = DisplaySystem.getDisplaySystem().getRenderer().createLightState();
        lightState.setTwoSidedLighting(true);
        DirectionalLight dl = new DirectionalLight();
        dl.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
        dl.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
        dl.setSpecular(ColorRGBA.white);
        dl.setDirection(new Vector3f(0, -600, 0));
        dl.setEnabled(true);
        lightState.attach(dl);

        lightState.setEnabled(true);
        rootNode.setRenderState(lightState);
    }

    private void initSkyBox() {
        skyBox = new Skybox("skybox", 10, 10, 10);

        skyBox.setTexture(Skybox.Face.North, TextureManager.loadTexture(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/skybox/north.jpg"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        skyBox.setTexture(Skybox.Face.West, TextureManager.loadTexture(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/skybox/west.jpg"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        skyBox.setTexture(Skybox.Face.South, TextureManager.loadTexture(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/skybox/south.jpg"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        skyBox.setTexture(Skybox.Face.East, TextureManager.loadTexture(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/skybox/east.jpg"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        skyBox.setTexture(Skybox.Face.Up, TextureManager.loadTexture(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/skybox/up.jpg"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        skyBox.setTexture(Skybox.Face.Down, TextureManager.loadTexture(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/skybox/down.jpg"),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear));
        skyBox.preloadTextures();


        LensFlare flare = getLensFlare();
        flare.setRootNode(rootNode);
        skyBox.attachChild(flare);

        rootNode.attachChild(skyBox);
    }

    private LensFlare getLensFlare() {

        TextureState[] tex = new TextureState[4];
        tex[0] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();

        tex[0].setTexture(TextureManager.loadTexture(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/lensflare/flare1.png"),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear,
                Format.RGBA8, 1.0f, true));
        tex[0].setEnabled(true);
        tex[0].apply();

        tex[1] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        tex[1].setTexture(TextureManager.loadTexture(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/lensflare/flare2.png"),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear));
        tex[1].setEnabled(true);
        tex[1].apply();

        tex[2] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        tex[2].setTexture(TextureManager.loadTexture(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/lensflare/flare3.png"),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear));
        tex[2].setEnabled(true);
        tex[2].apply();

        tex[3] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        tex[3].setTexture(TextureManager.loadTexture(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/lensflare/flare4.png"),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear));
        tex[3].setEnabled(true);
        tex[3].apply();

        LensFlare flare = LensFlareFactory.createBasicLensFlare("lensflare", tex);
        flare.setLocalTranslation(new Vector3f(180, 50, -20));

        return flare;
    }

    private void initTerrain() {
        Random r = new Random();
        FaultFractalHeightMap hm = new FaultFractalHeightMap(129, 128, 0, 128, r.nextFloat() * 0.8f + 0.2f);
        terrainPage = new TerrainPage("TerrainPage", 33, hm.getSize(), new Vector3f(20, 1, 20), hm.getHeightMap());
        terrainPage.setLocalTranslation(new Vector3f(0, 0, 0));
        terrainPage.setModelBound(new BoundingBox());
        terrainPage.updateModelBound();
        terrainPage.setDetailTexture(1, 16);
        terrainPage.setRenderState(getTerrainTextures(hm));
        rootNode.attachChild(terrainPage);
        VBOUtil.enableVBOonTree(terrainPage);
        rootNode.attachChild(terrainPage);
    }

    private TextureState getTerrainTextures(AbstractHeightMap hm) {
        ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
                hm);
        pt.addTexture(new ImageIcon(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/terrain/grass.jpg")),
                -128, 80, 220);//-128,128,255
        pt.addTexture(new ImageIcon(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/terrain/dirt.jpg")),
                80, 220, 270);//128,255,384
        pt.addTexture(new ImageIcon(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/terrain/high.jpg")),
                220, 270, 400);//255,384,512

        pt.createTexture(256);

        TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        ts.setEnabled(true);
        Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear, true);
        ts.setTexture(t1, 0);

        Texture t2 = TextureManager.loadTexture(getClass().getClassLoader().getResource("com/erickpardal/game/fpsdemo/data/textures/scene/terrain/detail.jpg"),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear);
        ts.setTexture(t2, 1);
        t2.setWrap(Texture.WrapAxis.S, Texture.WrapMode.Repeat);
        t2.setWrap(Texture.WrapAxis.T, Texture.WrapMode.Repeat);

        t1.setApply(Texture.ApplyMode.Combine);
        t1.setCombineFuncRGB(Texture.CombinerFunctionRGB.Modulate);
        t1.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
        t1.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
        t1.setCombineSrc1RGB(Texture.CombinerSource.PrimaryColor);
        t1.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);
        t1.setCombineScaleRGB(Texture.CombinerScale.One);

        t2.setApply(Texture.ApplyMode.Combine);
        t2.setCombineFuncRGB(Texture.CombinerFunctionRGB.AddSigned);
        t2.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
        t2.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
        t2.setCombineSrc1RGB(Texture.CombinerSource.Previous);
        t2.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);
        t2.setCombineScaleRGB(Texture.CombinerScale.One);

        return ts;
    }

    @Override
    protected void stateUpdate(float tpf) {
        super.stateUpdate(tpf);
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit", false)) {
            System.exit(0);
        }

        player.update();

        Vector3f pos = cam.getLocation();
        pos.y = terrainPage.getHeight(pos.x, pos.z) + 50;
        camNode.setLocalTranslation(pos);
        input.update(tpf);

        skyBox.getLocalTranslation().set(cam.getLocation());
        //dome.update(pos);
    }

    @Override
    protected void stateRender(float tpf) {
        super.stateRender(tpf);
    }
}
